package jmine.tec.utils.collection;

import java.math.BigDecimal;
import java.math.MathContext;

/**
 * Utilitary class for performing common fold operations on {@link BigDecimal}
 * 
 * @author jrenaut
 */
public final class FoldBigDecimalUtils {

    /**
     * @param iterable a {@link Iterable}
     * @return the multiplication of all entries in the {@link Iterable}
     */
    public static BigDecimal multiply(Iterable<BigDecimal> iterable) {
        return FoldUtils.leftFold(iterable, BigDecimal.ONE, DEFAULT_BIG_DECIMAL_MULT_FOLD);
    }

    /**
     * @param iterable a {@link Iterable}
     * @param mc the {@link MathContext} to be used 
     * @return the multiplication of all entries in the {@link Iterable}
     */
    public static BigDecimal multiply(Iterable<BigDecimal> iterable, MathContext mc) {
        return FoldUtils.leftFold(iterable, BigDecimal.ONE, new BigDecimalMultiplicationFold(mc));
    }
    
    /**
     * @param iterable a {@link Iterable}
     * @return the sum of all entries in the {@link Iterable}
     */
    public static BigDecimal sum(Iterable<BigDecimal> iterable) {
        return FoldUtils.leftFold(iterable, BigDecimal.ZERO, DEFAULT_BIG_DECIMAL_SUM_FOLD);
    }
    
    /**
     * @param iterable a {@link Iterable}
     * @param mc the {@link MathContext} to be used
     * @return the sum of all entries in the {@link Iterable}
     */
    public static BigDecimal sum(Iterable<BigDecimal> iterable, MathContext mc) {
        return FoldUtils.leftFold(iterable, BigDecimal.ZERO, new BigDecimalSumFold(mc));
    }
    
    public static final SymetricFoldOperation<BigDecimal> DEFAULT_BIG_DECIMAL_MULT_FOLD = new BigDecimalMultiplicationFold();

    public static final SymetricFoldOperation<BigDecimal> DEFAULT_BIG_DECIMAL_SUM_FOLD = new BigDecimalSumFold();

    /**
     * Abstract fold operation for BigDecimal
     * 
     * @author jrenaut
     */
    public abstract static class AbstractBigDecimalFold implements SymetricFoldOperation<BigDecimal> {

        private MathContext mc;

        /**
         * With default {@link MathContext} DECIMAL128
         */
        public AbstractBigDecimalFold() {
            this(MathContext.DECIMAL128);
        }

        /**
         * @param mc a desired {@link MathContext}
         */
        public AbstractBigDecimalFold(MathContext mc) {
            this.mc = mc;
        }

        /**
         * {@inheritDoc}
         */
        public BigDecimal apply(BigDecimal s, BigDecimal t) {
            return this.doApply(s, t, this.mc);
        }

        /**
         * @param s the first parameter
         * @param t the second parameter
         * @param mathContext a math context
         * @return the result of the operation
         */
        protected abstract BigDecimal doApply(BigDecimal s, BigDecimal t, MathContext mathContext);

    }

    /**
     * BigDecimal Multiplication Fold
     * 
     * @author jrenaut
     */
    public static final class BigDecimalMultiplicationFold extends AbstractBigDecimalFold {

        /**
         * With default {@link MathContext} DECIMAL128
         */
        public BigDecimalMultiplicationFold() {
        }

        /**
         * @param mc a {@link MathContext}
         */
        public BigDecimalMultiplicationFold(MathContext mc) {
            super(mc);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected BigDecimal doApply(BigDecimal s, BigDecimal t, MathContext mathContext) {
            return s.multiply(t, mathContext);
        }

    }

    /**
     * BigDecimal Sum Fold
     * 
     * @author jrenaut
     */
    public static final class BigDecimalSumFold extends AbstractBigDecimalFold {

        /**
         * With default {@link MathContext} DECIMAL128
         */
        public BigDecimalSumFold() {
        }

        /**
         * @param mc a {@link MathContext}
         */
        public BigDecimalSumFold(MathContext mc) {
            super(mc);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected BigDecimal doApply(BigDecimal s, BigDecimal t, MathContext mathContext) {
            return s.add(t, mathContext);
        }

    }

    /**
     */
    private FoldBigDecimalUtils() {
        super();
    }

}
